Mega mBot Control System and Installation Doc

Last updated: 10/18/2023


This document is used to describe the installation and use of the Mega mBot control system. Please use the following quick links to a specific section. Note that the system is developed in Python, so all examples in this document are coded in Python.

Installation and Setup

1.) Please download client.zip and server.zip, and save them to your local file system.

2.) If planning to use the desktop as the client host (not recommended, a Raspberry Pi host is prefered), a Linux based installation is required, as some modules used are not compatible with Windows and MacOS installations.

Initial setup

A Raspberry Pi is mounted on top of a Mega mBot. Generally speaking, server.py runs on the Raspberry Pi and is used to control the mobility, communication, and computation of the Mega Pi. Hence, this section will show how to install Raspberry Pi OS on a new Raspberry Pi and set it up via SSH.

1.) Download a Legacy installation (Buster) of Raspberry Pi Lite OS from here. It is recommended to download Raspberry Pi OS Lite.

2.) Download balenaEtcher from here, which is OS Image Flasher for SD Cards and USB Drives.

3.) Install Raspberry Pi OS to the SD card of Raspberry Pi via balenaEtcher.

etcher

4.) Insert the SD card back into the Raspberry Pi and connect the Raspberry Pi to the internet via an ethernet cable.

5.) Check the IP of the Pi to ssh into it

$ ifconfig

6.) Connect via ssh with username pi to the IP address (e.g., 192.168.1.237) you found in step 6. The default password is raspberry.

$ ssh pi@192.168.1.237

7.) Install Python 3.5 and the libraries we will need to the Raspberry Pi:

pi@raspberrypi:~ $ sudo apt update
pi@raspberrypi:~ $ sudo apt-get update && sudo apt-get upgrade -y
pi@raspberrypi:~ $ sudo apt install build-essential tk-dev
pi@raspberrypi:~ $ sudo apt install libncurses5-dev libncursesw5-dev libreadline6-dev
pi@raspberrypi:~ $ sudo apt install libdb5.3-dev libgdbm-dev libsqlite3-dev libssl-dev
pi@raspberrypi:~ $ sudo apt install libbz2-dev libexpat1-dev liblzma-dev zlib1g-dev
pi@raspberrypi:~ $ sudo apt-get install libatlas-base-dev

pi@raspberrypi:~ $ wget https://www.python.org/ftp/python/3.5.3/Python-3.5.3.tgz
pi@raspberrypi:~ $ tar zxvf Python-3.5.3.tgz
pi@raspberrypi:~ $ cd Python-3.5.3
pi@raspberrypi:~/Python-3.5.3 $ ./configure --prefix=/usr/local/opt/python-3.5.3
pi@raspberrypi:~/Python-3.5.3 $ make -j 4
pi@raspberrypi:~/Python-3.5.3 $ sudo make altinstall
pi@raspberrypi:~/Python-3.5.3 $ sudo update-alternatives –install /usr/bin/python python /home/pi/Python-3.5.3/python 3
pi@raspberrypi:~ $ sudo ln -s /usr/local/opt/python-3.5.3/bin/pydoc3.5 /usr/bin/pydoc3.5
pi@raspberrypi:~ $ sudo ln -s /usr/local/opt/python-3.5.3/bin/python3.5 /usr/bin/python3.5
pi@raspberrypi:~ $ sudo ln -s /usr/local/opt/python-3.5.3/bin/python3.5m /usr/bin/python3.5m
pi@raspberrypi:~ $ sudo ln -s /usr/local/opt/python-3.5.3/bin/pyvenv-3.5 /usr/bin/pyvenv-3.5
pi@raspberrypi:~ $ sudo ln -s /usr/local/opt/python-3.5.3/bin/pip3.5 /usr/bin/pip3.5
pi@raspberrypi:~ $ sudo ln -s /usr/local/opt/python-3.5.3/bin/pip3.5 /usr/bin/pip

Note that the command for Numpy is different depending on version of Python. use the python3 command for 3, or python for 2.7.

pi@raspberrypi:~ $ sudo apt install python-numpy
pi@raspberrypi:~ $ sudo apt install python3-numpy
pi@raspberrypi:~ $ python3 -m pip install cython --upgrade
pi@raspberrypi:~ $ pip install future-fstrings 
pi@raspberrypi:~ $ python2.7 -m pip install future-fstrings
pi@raspberrypi:~ $ pip install https://pypi.python.org/packages/source/g/getch/getch-1.0-python2.tar.gz#md5=586ea0f1f16aa094ff6a30736ba03c50
pi@raspberrypi:~ $ pip3 install py-getch
pi@raspberrypi:~ $ pip install makeblock==0.1.7
pi@raspberrypi:~ $ pip3 install makeblock==0.1.7

iPerf Network Testing

To make sure that the iPerf latency testing can be used, make sure to install the below files. Note that only 2 pis can use iPerf3 at a time, as muliple nodes are not supported.

pi@raspberrypi:~ $ wget https://downloads.es.net/pub/iperf/iperf-3.10.1.tar.gz
pi@raspberrypi:~ $ tar xvf iperf-3.10.1.tar.gz iperf-3.10.1/
pi@raspberrypi:~ $ cd iperf-3.10.1
pi@raspberrypi:~/iperf-3.10.1 $ ./configure
pi@raspberrypi:~/iperf-3.10.1 $ make
pi@raspberrypi:~/iperf-3.10.1 $ cd ..
pi@raspberrypi:~ $ sudo apt-get install lib32z1
pi@raspberrypi:~ $ sudo apt install -y iperf3
pi@raspberrypi:~ $ pip3 install iperf3

Bluetooth

pi@raspberrypi:~ $ sudo apt-get install bluetooth libbluetooth-dev
pi@raspberrypi:~ $ sudo python3 -m pip install pybluez

10.) Install pip into the Python 3.x that comes with the Raspberry Pi.

pi@raspberrypi:~ $ sudo apt update
pi@raspberrypi:~ $ sudo apt install python3-distutils python3-apt
pi@raspberrypi:~ $ wget https://bootstrap.pypa.io/get-pip.py
pi@raspberrypi:~ $ curl -O https://bootstrap.pypa.io/pip/2.7/get-pip.py
pi@raspberrypi:~ $ python get-pip.py
pi@raspberrypi:~ $ python -m pip install --upgrade "pip < 21.0"
pi@raspberrypi:~ $ sudo apt install python3-pip
pi@raspberrypi:~ $ sudo apt install python-pip

11.) Clean workspace

pi@raspberrypi:~ $ rm iperf-3.10.1.tar.gz
pi@raspberrypi:~ $ rm Python-3.5.3.tar.gz
pi@raspberrypi:~ $ rm get-pip.py

Client

On the client side (desktop or laptop), you can run the client.py as follows:

$ python3 lanclient.py
Welcome to use Mega mBot control client.

1). Control all Mega mBots in this network 2). Control a single Mega mBot in this network

Select from 1 to 2:

1.) The first option connects to all Mega mBots in your network, which means as long as your Mega mBot (running server.py) is connected to the same network as your client, you can send instructions to all Mega mBots synchronously. This is useful when you want all Mega mBots to perform the same mobility moves as well as distribute computing tasks.

2.) The second option only connects to a single Mega mBot, which is useful when you want to test instructions.

Available Control Instructions

Server

On the server side (Raspberry Pi on the top of Mega mBot), you can run the server.py as follows:

pi@raspberrypi:~/server $ sudo python3 lanserver.py
Mega mBot infos:
 - Address: '192.168.1.150:8085'
 - The size of per recv in bytes: '2048'
 - Folder to store uploaded files: 'upload'
 - CPU: 'ARMv7 Processor rev 4 (v7l)'
 - RAM: '1Gi'

Mega mBot is Waitiing for a Connection..

Configuration

The config.txt file is the configuration file that used to set up the server. The most important things in this file are the host IP address and port settings. Here is the default settings:

config

mBotMega Module

The class mBotMega in lib/mbot.py is the interface that provides methods to control the mobility of a Mega mBot. When you look at the code, each method is well commented for the parameters.

Lets try running an example program.

$ python client.py
Welcome to use Mega mBot control client.

1). Control all Mega mBots in this network
2). Control a single Mega mBot in this network

Select from 1 to 2: 2
Some Mega mBot nodes is pre-stored in this computer.
Do you want to rescan? (y/n): n

Which of the following Mega mBot nodes do you want to connect to?

  1). '192.168.1.114:8085'
  2). '192.168.1.150:8085'

Select from 1 to 2: 2
Trying to Connect to 192.168.1.150:8085

Welcome to Use Mega mBot Remote Control System

Mega mBot infos:
 - Address: '192.168.1.150:8085'
 - The size of per recv in bytes: '2048'
 - Folder to store uploaded files: 'upload'
 - CPU: 'ARMv7 Processor rev 4 (v7l)'
 - RAM: '1Gi'

Instruction: update
Firmware location: mobility/test2.py
Instruction completed!
Instruction: run 
The Mega mBot is now running.

Now, when you trigger the front IR proximity sensor, the Mega mBot will move backwards, and when you trigger the back impact switches, the Mega mBot will perform a different series of movements. Lastly, when you are done with the testing, you should stop the execution of this mobility firmware by:

Instruction: stop
Instruction completed!
Also, you can check the status of the Mega mBot to make sure that the mobility firmware is exactly stopped.
Instruction: status
Current Status of Mega mBot:
 - Movement: The Mega mBot is not running.
 - Computation: The Mega mBot is not computing any task.

Custom Mobility Controls

As you have already seen the mobility control example at the section mBotMega Module, it is possible to create custom mobility firmware to be installed at each Mega mBot. To create such firmware, you need firstly create a Mobility class in a new Python file. Once you have the Mobility class, you have to include a __init__ method that must contain exactly one parameter to which an instance of mBotMega Module will be passed. Lastly, the Mobility class must include a run method, which is executed when the client trigger the mobility firmware. In addition, if you want to import any module in the mobility firmware, you must import inside the run method. Otherwise, it won't work. Here is a sample template:

class Mobility:
  def __init__(self, mbot):
    self.mbot = mbot

  def run(self):
    from time import sleep
    while(1):

      # Your code continue here

      sleep(1)

Setting Up Different Methods

Note that there are currently three(four if including ROS) methods of controlling the Mega Pi systems as a group.
Method 1 - Network Sockets This is the simplest method to use the Mbots together, although it is slower than MPI, and a network is required, so places without a LAN network are incompatible.
sudo chgrp blutooth /var/run/sdp Method 2 - Bluetooth Sockets The solution to using sockets outside of a LAN network is using Bluetooth, although the downside here is that there is inevitably a minor amount of latency due to the use case of bluetooth. Usually this will be a negligible difference, but over larger data sizes, it will cause an impact. The upside is that bluetooth connection is possible without a LAN network, making it deployable anywhere.
Method 3- MPI (Message Passing Interface) MPI, or Message Passing Interface, is the quickest of the three methods, being able to compute tasks at considerably faster times. The downside is the difficulty to program tasks in this method, as some form of knowledge of MPI is required. A network of some kind is also required

Setting up bluetooth

If you want to use the bluetooth server and client file you follow the steps below to install and setup the necessary blutooth parts. Using the btclient.py and btserver.py files functions identical to that of the LAN network version.

1.) Edit the /boot/config.txt

pi@raspberrypi:~ $ sudo  nano /boot/config.txt

2.) At the bottom bottom and change enable-uart to 1, and add dtoverlay=pi3-miniuart-bt

3.) Reboot and check if the bluetooth is enabled to the Pi

pi@raspberrypi:~ $ cat /etc/group |  grep bluetooth

3.5.) If not, add the Pi to the blutooth group

pi@raspberrypi:~ $ sudo usermod -G bluetooth -a pi
pi@raspberrypi:~ $ sudo chgrp bluetooth /var/run/sdp
pi@raspberrypi:~ $ sudo nano /etc/systemd/var-run-sdp.path
[Unit]
Description=Monitor /var/run/sdp

[Install]
WantedBy=bluetooth.service

[Path]
PathExists=/var/run/sdp
Unit=var-run-sdp.service
pi@raspberrypi:~ $ sudo nano /etc/systemd/var-run-sdp.service
[Unit]
Description=Set permission of /var/run/sdp

[Install]
RequiredBy=var-run-sdp.path

[Path]
Type=simple
ExecStart=/bin/chgrp bluetooth /var/run/sdp

4.5) Start the bluetooth service

pi@raspberrypi:~ $ sudo systemctl daemon-reload
pi@raspberrypi:~ $ sudo systemctl enable var-run-sdp.path
pi@raspberrypi:~ $ sudo systemctl enable var-run-sdp.service
pi@raspberrypi:~ $ sudo systemctl start var-run-sdp.path

5.) This could break after updates, so make sure it doesn't by editting the override file as follows

pi@raspberrypi:~ $ sudo nano /etc/systemd/system/bluetooth.service.d/override.conf
[Service]
ExecStart=
ExecStart=/usr/lib/bluetooth/bluetoothd -C

5.5) Note that on restarts, the bluetooth may need to be restarted with the following command. This is just a quirk of the Raspberry Pi with mini-uart unfortunately

pi@raspberrypi:~ $ sudo hciconfig hci0 piscan

Installing mpi4py

One of the other methods used is MPI (Message Passing Interface), for this we are using MPICH and mpi4py on the Raspberry Pi. There are plenty of good tutorials online, such as this installation. However, we will also go over it here.

pi@raspberrypi:~ $ sudo raspi-config

1.) Change the name of all Pis being used under Advanced Options. For example, we used Pi1, Pi2, Pi3,etc. You can also use "master", slave1,slave2, etc, or whatever will be easy to remember.

2.) Setup passwordless ssh so that there is no conflict when attempting to use multiple machines.

pi@master:~ $ ssh-keygen -t rsa
pi@master:~ $ ssh-copy-id "other node IP"

2.5) Run ssh-copy-id for each node in your series.

3.) On each Pi, the /etc/hosts file needs to have the hostname and IP of the others, so,

pi@master:~ $ sudo nano /etc/hosts

3.5.) Add the following below the other text, where "node IP" is the IP of a Pi, and "hostname" is the name given on that Pi

"node IP" 5 spaces (tab) "hostname"
192.168.0.1     master

4.) Install MPICH and mpi4py on all Pis

pi@master:~ $ sudo apt install mpich
pi@master:~ $ pip3 install mpi4py

To test that it is working correctly, use

pi@master:~ $ mpiexec -n 1 hostname

5.) Along with the host file, a machinefile is required to hold the IPs being used by mpi. This is done by simply writing the IPs or hostnames in a file titled machinefile where you want to run a program. This must be in the same folder as where you are running the mpiexec command. Alternatively, you can simply point to its location with "./foldername/machinefile"

pi@master:~ $ nano machinefile
192.168.0.1
192.168.0.2
192.168.0.3
192.168.0.4

6.) Try the hostname command with the machine file. If everything is set up properly on each Pi, the name given to each IP will be printed to the screen.

pi@master:~ $ mpiexec -n 4 -hostfile machinefile hostname 

Installing ROS Noetic

https://varhowto.com/install-ros-noetic-raspberry-pi-4/

Custom Tasks and how to use them

  • Custom Tasks
  • Using Task Files
  • Custom Tasks

    It is also possible to create custom tasks and distribute each sub-task to each connected Mega mBots and later reconstruct the result of each sub-task. To do this, there two steps:

    1.) On the client side, create a function task in a new Python file. This file is used for each Mega mBot to compute the sub-task distributed to it. Hence, it must contain only one parameter, which is used to pass the data assigned to it. Once the computation is done, it must return the result, which will later be collected and reconstructed on the client (a.k.a. the master node). Similar to the run method inside the Mobility class, if you want to import any module, you must import inside this task function. Here is example of computing uncoded convolution:

    def task(data):
      import numpy as np
      s = data[0]
      A = data[1:1+s]
      X = data[1+s:]
      rs = np.convolve(A, X)
      return rs
    

    2.) On the client side again (which is also the master node), you should edit the code on ./master.py for data decomposition, result reconstruction, and ways to get useful information of the task. This means you must have at least three functions: data_decompose, reconstruct and get_information. The first function data_decompose is used to decompose data into small piece and prepare to send to each connect Mega mBot, and it has two parameters. The number of connected Mega mBots passed by the first parameter, and the state (communication and computation latency) per Mega mBot is passed by the second parameter. The second function reconstruct is used to reconstruct results return by Mega mBots. Thus, it has only one parameter, which is used to pass the results. The get_information function is used to return useful information of the task (e.g., size of A), and it is okay to return None if the needed information is pre-stored in data_decompose and reconstruct. The following is the example of data decomposition and result reconstruction for uncoded convolution:

    import math
    import numpy as np
    
    def data_decompose(proc_num, state = None):
      size_of_A, size_of_X = (40000, 20000)
      s = int(math.sqrt((size_of_A * size_of_X) / proc_num))
    
      np.random.seed(35)
      A = np.random.randint(1000, size=size_of_A).tolist()
      X = np.random.randint(1000, size=size_of_X).tolist()
    
      iter1 = int(size_of_A / s)
      iter2 = int(size_of_X / s)
    
      data_list = []
      for i in range(0, iter1):
        for j in range(0, iter2):
          data = [s]
          data += A[i * s:(i + 1) * s]
          data += X[j * s:(j + 1) * s]
          data_list.append(data)
    
      return data_list
    
    def reconstruct(data_list):
      proc_num = len(data_list)
      size_of_A, size_of_X = (40000, 20000)
      s = int(math.sqrt((size_of_A * size_of_X) / proc_num))
    
      iter1 = int(size_of_A / s)
      iter2 = int(size_of_X / s)
    
      n_A = s + size_of_X - 1
      n_Z = len(data_list[0])
    
      result = []
      for i in range(0, iter1):
        data = []
        partial_result = [0 for i in range(0, n_A)]
        for j in range(0, iter2):
          padding_left = s * j
          padding_right = n_A - n_Z - s * j
          data_temp = data_list[i*iter2+j]
          data_temp = np.pad(data_temp, (padding_left, padding_right), 'constant')
          data.append(data_temp)
        for d in data:
          partial_result = np.add(partial_result, d)
        result.append(partial_result)
    
      n = size_of_A + size_of_X - 1
      n_Z = len(result[0])
    
      data = []
      final_result = [0 for i in range(0, n)]
      for j in range(0, len(result)):
        padding_left = s * j
        padding_right = n - n_Z - s * j
        data_temp = np.pad(result[j], (padding_left, padding_right), 'constant')
        data.append(data_temp)
    
      for d in data:
        final_result = np.add(final_result, d)
    
      # return final_result
      return np.round(final_result).astype(int)
    
    def get_information():
      pass
    

    Note: If the data returned by the data_decompose function is in numpy array format, be sure to convert it to a list through .tolist().

    Using Task Files

    1.) Copy-paste the function task from the first step of the Custom Tasks section and save it to a new Python file ./task/uncoded1.py.

    2.) Copy-paste all code from the second step of the Custom Tasks section to the Python file ./master.py.

    3.) On the client side, run:
    $ python3 client.py
    Welcome to use Mega mBot control client.

    1). Control all Mega mBots in this network 2). Control a single Mega mBot in this network

    Select from 1 to 2: 1 Now scan all available Mega mBots... 2022-03-15 15:12:39: Start scanning... - 192.168.1.114:8085 is available - 192.168.1.150:8085 is available

    2022-03-15 15:13:05: Scan completed. Trying to Connect to 192.168.1.114:8085 Trying to Connect to 192.168.1.150:8085 Connected to all available Mega mBots. Instruction: task Task file location: task/uncoded1.py - ins to 192.168.1.114: task - ins to 192.168.1.150: task Final Result: [ 581095 863278 1330104 ... 793678 449146 109648] >> Total time (computation + communication + reconstruction): 3.90215s <<


    Appendix - Useful Reference